Skip to content

feat(assessors): add lint suppression density assessor - #518

Merged
kami619 merged 1 commit into
ambient-code:mainfrom
msu8:510
Jul 28, 2026
Merged

feat(assessors): add lint suppression density assessor#518
kami619 merged 1 commit into
ambient-code:mainfrom
msu8:510

Conversation

@msu8

@msu8 msu8 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Description

Add LintSuppressionAssessor to detect overuse of lint suppression directives
across the codebase. A repo can have a comprehensive lint config and still
render it meaningless through blanket //nolint, # noqa, or
// eslint-disable usage — lint passes, but not because the code is clean.
This gives AI agents a false signal that the codebase is healthy.

Type of Change

  • New feature (non-breaking change which adds functionality)
  • Documentation update
  • Test coverage improvement

Related Issues

Fixes #510

Changes Made

  • New LintSuppressionAssessor (lint_suppression_density, Tier 3, 2% weight)
  • Scans source files for suppression directives across 11 languages: Go (//nolint), Python (# noqa, # type: ignore, # pylint: disable), JavaScript/TypeScript (// eslint-disable, // @ts-ignore), Ruby (# rubocop:disable), Java (@SuppressWarnings), Terraform (# tflint-ignore:), Shell (# shellcheck disable=), Dockerfile (# hadolint ignore=), YAML (# yamllint disable, # kube-linter disable), Markdown (<!-- markdownlint-disable -->)
  • Normalizes count per 1,000 LOC; scores 100 at ≤5/1k, 0 at ≥15/1k, linear in between
  • Thresholds and test-file exclusion configurable via .agentready-config.yaml::assessor_options
  • Excludes vendor/, node_modules/, and other generated directories from scanning
  • 47 unit tests covering all 11 languages, pass/fail/partial scoring, custom thresholds, test exclusion, excluded dirs, evidence content, and attribute metadata
  • docs/attributes.md updated with scoring rules, language coverage, and remediation examples
  • default-weights.yaml updated (v2.5.0, 30 total attributes)
  • Consolidated into code_quality.py — no separate module

Testing

  • Unit tests pass (pytest)
  • Integration tests pass
  • Manual testing performed
  • No new warnings or errors

Manual test: assessed psf/black (Python), golangci/golangci-lint (Go) and others

image image

Checklist

  • My code follows the project's code style
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Additional Notes

Summary by CodeRabbit

  • New Features
    • Added “Lint Suppression Density” assessment with multi-language scanning, suppression density scoring, evidence reporting, and remediation guidance.
  • Documentation
    • Expanded the attributes overview to 30 total, updated tier counts/weights, and updated implementation status for the full assessor set.
  • Tests
    • Added unit coverage for suppression detection, scoring thresholds (including boundary cases), test-file exclusions, and assessor registration.
  • Chores
    • Updated default weights and rebalanced related tier weight contributions.
    • Enhanced language detection to recognize additional extensions and extensionless filenames (e.g., Dockerfile).

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a configurable LintSuppressionAssessor that scans tracked source files, calculates suppression density per KLOC, reports evidence and remediation, registers a weighted Tier 3 attribute, and updates documentation and tests.

Changes

Lint suppression density

Layer / File(s) Summary
Configuration and language detection
src/agentready/models/config.py, .agentready-config.example.yaml, src/agentready/data/.agentready-config.example.yaml, src/agentready/services/language_detector.py
Adds validated thresholds, test exclusion options, and detection for additional extensions and named files.
Suppression scanning and scoring
src/agentready/assessors/code_quality.py
Scans git-tracked source files, applies exclusions and scan-health checks, calculates suppression density, and returns evidence-backed findings.
Registration and weighting
src/agentready/assessors/__init__.py, src/agentready/data/default-weights.yaml, src/agentready/assessors/structure.py
Registers the Tier 3 assessor and rebalances cyclomatic-complexity and architectural-boundary weights.
Documentation and validation
docs/attributes.md, tests/unit/test_assessors_code_quality.py, tests/unit/test_assessors_structure.py, tests/unit/test_language_detector.py
Documents the 30-assessor lineup and tests detection, scoring, filtering, configuration, evidence, robustness, metadata, and registration.

Sequence Diagram(s)

sequenceDiagram
  participant Repository
  participant LanguageDetector
  participant LintSuppressionAssessor
  participant Git
  participant Finding
  Repository->>LanguageDetector: detect tracked repository languages
  LanguageDetector-->>LintSuppressionAssessor: return applicable languages
  LintSuppressionAssessor->>Git: list tracked source files
  Git-->>LintSuppressionAssessor: return file inventory
  LintSuppressionAssessor->>LintSuppressionAssessor: count suppressions and LOC
  LintSuppressionAssessor->>Finding: return score, evidence, and remediation
Loading

Possibly related PRs

Suggested labels: released

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses the required Conventional Commits format and accurately describes the new lint suppression density assessor.
Linked Issues check ✅ Passed The PR implements the required language coverage, per-1,000 LOC scoring, configurable thresholds, optional test exclusion, and top-file evidence.
Out of Scope Changes check ✅ Passed The docs, tests, weight updates, and language-detection changes all support the new assessor, so no unrelated changes stand out.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@msu8
msu8 force-pushed the 510 branch 2 times, most recently from f32905b to 3962946 Compare July 16, 2026 14:25
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

📈 Test Coverage Report

Branch Coverage
This PR 76.1%
Main 75.8%
Diff ✅ +0.3%

Coverage calculated from unit tests only

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/attributes.md`:
- Line 6: Update the attribute counts in docs/attributes.md to match the weights
catalog: report 30 total attributes, 10 Tier 2 attributes, and 9 Tier 3
attributes. Adjust the affected summary and section text while leaving the
attribute definitions unchanged.

In `@src/agentready/assessors/code_quality.py`:
- Around line 2149-2161: Update the suppression assessor flow around
_count_file_suppressions to retain each file’s line count, rank file_stats by
suppressions divided by LOC rather than absolute counts, and use
calculate_proportional_score() for proportional scoring. Replace direct Finding
construction and manual score interpolation with Finding.create_pass() or
Finding.create_fail() while preserving the assessor’s existing pass/fail
criteria and output details.
- Around line 1943-1950: Update the JavaScript and TypeScript rule patterns in
the code-quality assessor to detect canonical block-form /* eslint-disable */
directives alongside existing line comments, while retaining `@ts-ignore`
detection. Add a regression test covering block-form ESLint suppression and
confirming it is counted.

In `@src/agentready/data/default-weights.yaml`:
- Around line 76-81: Update
CyclomaticComplexityAssessor.attribute.default_weight and
ArchitecturalBoundaryAssessor.attribute.default_weight from 0.02 to 0.01 so
assessor fallback values match the corresponding entries in
default-weights.yaml.

In `@src/agentready/models/config.py`:
- Around line 112-118: Add a typed LintSuppressionOptions model near the
configuration models and change assessor_options to use it instead of
unvalidated Any dictionaries. Configure strict boolean fields, finite
non-negative numeric thresholds, and validation requiring fail_per_kloc to
exceed pass_per_kloc, so invalid assessor settings are rejected during
configuration parsing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 007af8eb-0b73-4298-aec6-3d28976b37ae

📥 Commits

Reviewing files that changed from the base of the PR and between 108e5aa and b4eec9d.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock, !uv.lock
📒 Files selected for processing (8)
  • .agentready-config.example.yaml
  • docs/attributes.md
  • src/agentready/assessors/__init__.py
  • src/agentready/assessors/code_quality.py
  • src/agentready/data/.agentready-config.example.yaml
  • src/agentready/data/default-weights.yaml
  • src/agentready/models/config.py
  • tests/unit/test_assessors_code_quality.py

Comment thread docs/attributes.md Outdated
Comment thread src/agentready/assessors/code_quality.py
Comment thread src/agentready/assessors/code_quality.py
Comment thread src/agentready/data/default-weights.yaml
Comment thread src/agentready/models/config.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/agentready/assessors/code_quality.py`:
- Around line 2119-2127: Update the traversal loop around os.walk in the
relevant assessor to use the repository’s tracked, ignore-aware file inventory
instead of scanning every directory. Preserve the existing
_SUPPRESSION_EXCLUDED_DIRS filtering and filename matching for bare_names and
dot_exts, while retaining explicit generated/vendor exclusions and yielding the
same abs_path, rel_path pairs.
- Around line 2135-2140: Update the suppression-counting logic around the
lines-processing function to ignore directive text inside string literals,
including test fixtures and statements such as print calls. Tokenize comment
content or mask string literals before applying patterns, while preserving
counting of actual suppression directives in comments and the existing return
values.
- Around line 2096-2103: Update the JavaScript/TypeScript branch in the
test-file detection logic to recognize both JSX and TSX test/spec suffixes in
addition to JS and TS. Ensure exclude_tests correctly filters Component.test.jsx
and Component.spec.tsx while preserving the existing directory and root-prefix
checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: f728a4b2-731f-461b-b068-6702d5837e7c

📥 Commits

Reviewing files that changed from the base of the PR and between b4eec9d and 3962946.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock, !uv.lock
📒 Files selected for processing (8)
  • .agentready-config.example.yaml
  • docs/attributes.md
  • src/agentready/assessors/__init__.py
  • src/agentready/assessors/code_quality.py
  • src/agentready/data/.agentready-config.example.yaml
  • src/agentready/data/default-weights.yaml
  • src/agentready/models/config.py
  • tests/unit/test_assessors_code_quality.py

Comment thread src/agentready/assessors/code_quality.py Outdated
Comment thread src/agentready/assessors/code_quality.py Outdated
Comment thread src/agentready/assessors/code_quality.py Outdated
@msu8
msu8 force-pushed the 510 branch 4 times, most recently from 5f9c4bb to 476098c Compare July 17, 2026 06:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/agentready/assessors/code_quality.py (2)

2080-2087: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Exclude JSX and TSX test files.

Component.test.jsx and Component.spec.tsx are scanned even when exclude_tests=True. Derive suffixes from _LANG_EXTENSIONS[lang] and add regressions for both forms.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agentready/assessors/code_quality.py` around lines 2080 - 2087, Update
the test-file detection logic in the JavaScript/TypeScript branch to derive test
suffixes from _LANG_EXTENSIONS[lang], covering JSX and TSX alongside JS and TS.
Preserve the existing directory and root-prefix checks, and add regression
coverage confirming Component.test.jsx and Component.spec.tsx are excluded when
exclude_tests=True.

2159-2173: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Rank files by suppression concentration, not count.

A large file with more suppressions can outrank a small file with much higher density. Retain each file’s LOC and sort by suppressions / LOC, as required by the feature objective.

Also applies to: 2200-2207

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agentready/assessors/code_quality.py` around lines 2159 - 2173, Update
the suppression file-statistics flow around _count_file_suppressions to retain
each file’s line count alongside its suppression count and rank files by
suppression concentration (sup_count divided by line_count), rather than
absolute suppression count. Apply the same change to the related statistics
handling around the additional referenced section, while preserving existing
test-file exclusion and zero-suppression behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/attributes.md`:
- Line 29: Update the attribute-count statements in docs/attributes.md to report
30 instead of 28, keeping them consistent with the 30-entry weights catalog and
the count stated on Line 6.

In `@src/agentready/models/config.py`:
- Around line 152-165: Unify lint-suppression configuration so the documented
assessor_options entry is not ignored: in src/agentready/models/config.py lines
152-165, expose one canonical path or validate/map the nested configuration into
LintSuppressionOptions; in src/agentready/assessors/code_quality.py lines
2061-2066, update the lint-suppression assessor to consume that canonical path
consistently; and in docs/attributes.md lines 1524-1532, document the same path
and configuration contract.

---

Duplicate comments:
In `@src/agentready/assessors/code_quality.py`:
- Around line 2080-2087: Update the test-file detection logic in the
JavaScript/TypeScript branch to derive test suffixes from
_LANG_EXTENSIONS[lang], covering JSX and TSX alongside JS and TS. Preserve the
existing directory and root-prefix checks, and add regression coverage
confirming Component.test.jsx and Component.spec.tsx are excluded when
exclude_tests=True.
- Around line 2159-2173: Update the suppression file-statistics flow around
_count_file_suppressions to retain each file’s line count alongside its
suppression count and rank files by suppression concentration (sup_count divided
by line_count), rather than absolute suppression count. Apply the same change to
the related statistics handling around the additional referenced section, while
preserving existing test-file exclusion and zero-suppression behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 0dbc6cd0-9e24-414d-aaaa-db0f3f0c65ae

📥 Commits

Reviewing files that changed from the base of the PR and between 3962946 and 476098c.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock, !uv.lock
📒 Files selected for processing (9)
  • .agentready-config.example.yaml
  • docs/attributes.md
  • src/agentready/assessors/__init__.py
  • src/agentready/assessors/code_quality.py
  • src/agentready/assessors/structure.py
  • src/agentready/data/.agentready-config.example.yaml
  • src/agentready/data/default-weights.yaml
  • src/agentready/models/config.py
  • tests/unit/test_assessors_code_quality.py

Comment thread docs/attributes.md Outdated
Comment thread src/agentready/models/config.py Outdated
@msu8
msu8 force-pushed the 510 branch 2 times, most recently from cf2229e to 47fe732 Compare July 17, 2026 06:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/agentready/assessors/code_quality.py`:
- Around line 2068-2094: Update _is_test_file to apply the shared
_TEST_DIR_FRAGMENTS and _TEST_ROOT_PREFIXES checks for every supported language
before language-specific filename rules. Add Java handling and ensure Java files
under test directories are excluded, while preserving existing Go, Python,
JavaScript, TypeScript, and Ruby filename detection; extend tests to cover the
additional supported languages.
- Around line 2111-2137: Update the file-discovery logic around
safe_subprocess_run and the os.walk fallback so Git failures, missing Git, or
timeouts do not trigger ignore-unaware traversal. Instead, return the assessor’s
established skipped/error finding for unavailable discovery, or replace the
fallback with an ignore-aware mechanism while preserving normal git ls-files
behavior.
- Around line 1937-1953: Extend _SUPPRESSION_PATTERNS for Python to recognize
whole-file # ruff: noqa and # flake8: noqa directives, and for TypeScript to
recognize // `@ts-nocheck` and // `@ts-expect-error`. Add regression tests covering
each directive and confirming they contribute to suppression density.
- Around line 2121-2125: The source-file discovery loop should reject symlinks
and other non-regular files before yielding paths, and downstream assessment
reads must be bounded. Update the logic around the visible rel_path filtering
and its corresponding read path to validate regular files without following
symlinks, then stream each file with a strict maximum byte limit instead of
unbounded read_text().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: fbd34c4b-2f0e-4211-8ac8-260ab1093670

📥 Commits

Reviewing files that changed from the base of the PR and between 476098c and cf2229e.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock, !uv.lock
📒 Files selected for processing (9)
  • .agentready-config.example.yaml
  • docs/attributes.md
  • src/agentready/assessors/__init__.py
  • src/agentready/assessors/code_quality.py
  • src/agentready/assessors/structure.py
  • src/agentready/data/.agentready-config.example.yaml
  • src/agentready/data/default-weights.yaml
  • src/agentready/models/config.py
  • tests/unit/test_assessors_code_quality.py

Comment thread src/agentready/assessors/code_quality.py
Comment thread src/agentready/assessors/code_quality.py
Comment thread src/agentready/assessors/code_quality.py Outdated
Comment thread src/agentready/assessors/code_quality.py Outdated
@msu8

msu8 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Hi @jwm4, could you PTAL?

@kami619 kami619 self-assigned this Jul 28, 2026
@kami619

kami619 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

@jwm4 and @msu8 I am taking a look at this PR.

@kami619

kami619 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

🤖 AI-assisted review

Disclosure. This review was produced by an AI agent, not a human reviewer. Every finding below was reproduced empirically against commit 6b3581f before being reported — but treat the analysis as a starting point for human judgment, not as a verdict. Reproduction commands are included so you can verify each claim yourself.

Model Claude Opus 4.6 (claude-opus-4-6)
Harness Claude Code CLI
Skill /pr-review-toolkit:review-pr
Sub-agents code-reviewer, pr-test-analyzer, silent-failure-hunter, type-design-analyzer, comment-analyzer (5, run independently in parallel)
Scope git diff main..HEAD @ 6b3581f4f470a5f7feab345ccc98684598468656

Five specialist agents reviewed independently and converged on the same core defect from four different angles. Findings are deduplicated and ranked below.


🔴 Critical — merge blockers

1. The branch breaks CI

tests/unit/test_assessors_structure.py:1513 still asserts default_weight == 0.02, but structure.py:1403 was changed to 0.01 to fund the new attribute.

$ pytest tests/unit/test_assessors_structure.py -q
E   AssertionError: assert 0.01 == 0.02
1 failed, 74 passed

Green on main (75/75), red here. One-line fix.

2. Terraform and Dockerfile support is unreachable dead code

Found independently by all five agents.

LanguageDetector.EXTENSION_MAP has no .tf entry, and detection is suffix-only (path.suffix), so an extensionless Dockerfile can never match. repository.languages therefore can never contain "Terraform" or "Dockerfile" — making the pattern entries at code_quality.py:1961,1963 inert, since both is_applicable() and the scan loop intersect against that dict.

Reproduced end-to-end on a repo containing 20 # tflint-ignore and 3 # hadolint ignore= directives:

detected languages: {'Python': 3}
pass 100.0  '0 suppressions / 3 LOC (0.0/1k)'

This is advertised in three user-facing places that are now false:

The tests certify the broken behavior. test:1324 and :1344 hand-construct languages={"Terraform": 1} / {"Dockerfile": 1} — a state the scanner cannot emit, at a count below minimum_file_threshold = 3. They pass, and prove nothing about production.

3. Markdown/YAML in the denominator inverts the metric

Markdown and YAML are first-class scanned languages. Both are near-universally present (threshold is only 3 files) and both contribute their full line count to total_lines while carrying essentially zero suppressions. The metric is documented as "per 1,000 lines of source code".

Synthetic Go repo at a genuine 100 suppressions/1k lines, plus 24k lines of Markdown docs:

Scope Result
As shipped (Go + Markdown) pass, score 100.0 — 4.0/1k
Go source only fail, score 0.0 — 100.0/1k

The score flips 0 → 100 purely because the project is documented — the exact inverse of the intended signal. On this repo the dilution is 3x (0.2/1k as shipped vs 0.6/1k Python-only).

Compounding it: code_quality.py:2158 uses len(text.splitlines()), counting blank lines, whereas LanguageDetector.count_total_lines() deliberately excludes them — a further 15–25% denominator inflation. splitlines() also splits on \v \f \x1c \x1d \x1e \x85 
 
; a file with 100 form-feed separators counted 302 lines against 202 real ones. Every one of these errors pushes toward a passing score.

4. Three silent-failure paths under-count the numerator while still reporting pass/100

All reproduced:

  • lstat() OSErrorcontinue (:2136-2139). A sparse checkout lists index entries that aren't on disk; every one is silently discarded. A repo at a true 714/1k (maximal fail) reported pass, score 100.0, 0 suppressions / 1 LOC.
  • The 1 MB read cap (:2152). A 1.79 MiB big.go with 2000 //nolint past the cap → pass, 0 suppressions / 17,139 LOC (true: 32,001 lines). Large files are precisely where generated/vendored suppressions cluster.
  • _count_file_suppressions returns (0,0) on OSError — byte-identical to "a clean empty file". A mode-000 file holding 50 # noqa vanished entirely: absent from the count, the denominator, and the top-offenders list.

Note that test_file_read_is_byte_bounded asserts this silence is correct — it hides 20 real suppressions past a patched cap and asserts status == "pass" and "Total suppressions: 0". It should assert that truncation is disclosed.

For a density metric, a swallowed read is worse than a crash: it produces a falsely good certification, and nobody reads a passing finding's evidence.

5. AGENTS.md Guideline 7 — docs not synced for the two defunded weights

The diff does touch docs/attributes.md, but misses the two attributes it defunded.


🟡 Important

# Finding Location
6 All three threshold comparisons survive mutation. <=< at :2210 and :2217, >=> at :2212 each leave 63/63 green — no test sits on a boundary. A flip at :2217 silently converts every threshold-exact repo from pass to fail. Confirmed current behavior: density exactly 5.0 → pass/100, exactly 15.0 → fail/0. code_quality.py:2210-2217
7 .mjs, .cjs, .pyi, .pyx, .zsh trigger language detection but are never scanned_LANG_EXTENSIONS is a hand-maintained second copy of EXTENSION_MAP that has already drifted. An ESM-only JS repo scores 100 regardless of eslint-disable count. Also .mdx is in the assessor table but not the detector. code_quality.py:1972-1984
8 Config.assessor_options is dead public schema — zero readers in src/, tests/, docs/, and absent from both example configs. It punches a hole in extra="forbid": config written under it validates cleanly and does nothing, which is strictly worse than the loud typo it would otherwise be. It also serializes into every report via assessment.py:128 while CURRENT_SCHEMA_VERSION stays 1.0.0. Config schemas are one-way doors. models/config.py:159
9 Total measurement failure reports as not_applicable ("No source files found for supported languages") — on a repo the scanner just reported as 100% Go. Contradicts the scan and misdirects debugging. is_applicable() has already established the language is present. code_quality.py:2194
10 The bare except Exception around git ls-files hardcodes "missing git, not a git repo, or timeout" — but the likeliest real trigger is SubprocessSecurityError from safe_subprocess_run's 10 MB stdout cap, which git ls-files clears on a ~200k-path monorepo. The user is told to install git, which is installed. safe_subprocess_run_stream already exists for exactly this. ("not a git repo" is near-unreachable — Repository.__post_init__ rejects those already.) code_quality.py:2124
11 git ls-files is re-invoked once per detected language for identical output — 4 spawns on this repo, 9 on a polyglot monorepo, each with a 30s timeout. Hoist it out of the loop. code_quality.py:2116
12 Attribute.criteria hardcodes _SUPPRESSION_DEFAULTS while Finding.threshold honors config → a repo setting pass_per_kloc: 10.0 gets one report containing both "≤5.0" and "≤10.0/1k". code_quality.py:2056

🔵 Suggestions

  • The class docstring's "Other languages always include all files" (:2029) is false — _TEST_DIR_FRAGMENTS is checked before the per-language branch, so tests/foo.sh and spec/values.yaml are excluded. Only the filename-suffix heuristics are language-specific.
  • docs/attributes.md:1504 breaks house style — no **ID**/**Tier**/**Weight**/**Category**/**Status** block, no #### Definition / Why It Matters / Measurable Criteria, no **Tools** / **Citations**. Compare ### Threat Model immediately above. The inline-(id, weight) heading format is the convention for the bullet lists, not for full ### sections.
  • The example-config comment exclude_tests: false # set true to exclude test files from LOC count describes the wrong effect — the flag skips files entirely (numerator and denominator). Opposite-signed from what someone tuning it would predict. The LintSuppressionOptions docstring gets this right; the YAML comments contradict it.
  • _MAX_SUPPRESSION_FILE_BYTES = 1_000_000 # 1 MiB — that's 1 MB. Same in docs/attributes.md:1510. Either say "1 MB" or use 1 << 20.
  • default-weights.yaml:12 and :72 still say Tier 3 has 8 attributes; it's 9. (Line 3 was correctly bumped to 30.)
  • The changelog block labels this v2.5.0, but pyproject.toml is at 2.50.0 and lint_config_coverage — labeled v2.4.0 — actually shipped in 2.50.0. The version numbering in that block has drifted from real releases; consider keying entries by branch/issue instead.
  • pass_per_kloc uses gt=0, forbidding 0.0 — the legitimate strictest policy ("no suppressions tolerated"). ge=0 on pass, gt=0 on fail.
  • _get_options unpacks the validated model into tuple[float, float, bool] — two positionally-adjacent floats of identical type, reintroducing the transposition hazard the validator exists to prevent. Return the LintSuppressionOptions instead.
  • Consider frozen=True on LintSuppressionOptions: pydantic v2 doesn't re-run mode="after" validators on assignment, and _SUPPRESSION_DEFAULTS is a process-wide shared mutable instance.

✅ Strengths

  • The security posture is genuinely strong. git-only discovery with a deliberate no-fallback policy (well-commented at :2113 — exactly the kind of "why" comment that stops a future maintainer from helpfully adding the fallback back), lstat + stat.S_ISREG symlink rejection, bounded reads. Mutation probes confirmed the symlink and gitignore tests each catch their regression, and the symlink test's use of a gitignored target to isolate the behavior is well-judged.
  • _GitInventoryUnavailable is the best-designed type in the diff — it cleanly separates "cannot measure" (skipped) from "measured zero" (not_applicable) from "bug" (error), and makes the no-silent-fallback decision greppable.
  • Weight arithmetic is correct: 30 attributes summing to exactly 1.0, tiers 58/27/13/2, and the new attribute is properly funded by reductions elsewhere.
  • black, isort, ruff check all clean. Line coverage of the new code is ~97%.
  • Assessor registration follows the AGENTS.md 4-step checklist correctly.
  • The custom two-threshold interpolation instead of calculate_proportional_score() is a defensible deviation — the helper models a single threshold and can't express a pass/fail band.

Recommended sequence

  1. Fix the red test ([P0] Create Automated Demo #1) — unblocks CI.
  2. Decide Terraform/Dockerfile ([P0] Fix Critical Security & Logic Bugs from Code Review #2): add .tf + a filename rule to EXTENSION_MAP, or strip them from the pattern tables, the Attribute.description, the docs, and the two tests. Then add the guard that would have caught this pre-merge:
    def test_suppression_tables_agree_with_detector():
        assert set(_SUPPRESSION_PATTERNS) == set(_LANG_EXTENSIONS)
        assert set(_SUPPRESSION_PATTERNS) <= set(LanguageDetector.EXTENSION_MAP.values())
  3. Fix the denominator ([P0] Bootstrap AgentReady Repository on GitHub #3): exclude Markdown/YAML from LOC (or report per-language), and stop counting blank lines. This is the change that most affects what the assessor actually measures.
  4. Make silent skips visible ([P0] Report Header with Repository Metadata #4): thread a scan-health tally (files_missing, files_unreadable, files_truncated) through the walk, and emit it in evidence unconditionally — on pass as well as fail. If the un-scanned ratio exceeds ~10%, downgrade to skipped rather than reporting a density at all. A density computed over an unknown fraction of the codebase isn't a measurement.
  5. Derive _LANG_EXTENSIONS from EXTENSION_MAP ([P4] Research Report Generator/Updater Utility #7) — structurally eliminates this entire drift class.
  6. Drop assessor_options ([P4] Repomix Integration #8) — re-add it in the PR that first needs it, with a reader, a precedence rule, and a test.
  7. Sync the two weight docs ([P0] Improve HTML Report Design (Font Size & Color Scheme) #5), add boundary tests ([P3] Report Schema Versioning #6), re-run.

The security-critical paths here were clearly thought about carefully, and the no-fallback discipline around git ls-files is a good instinct. The gap is that the measurement paths weren't held to the same standard: the language table was written without checking it against the detector, and several tests confirm the implementation's assumptions rather than the behavior users will get.

Posted by an AI agent (Claude Opus 4.6 via Claude Code, /pr-review-toolkit:review-pr). Findings are reproducible — please verify before acting, and correct anything that's wrong.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unit/test_language_detector.py`:
- Around line 23-33: Rename test_bare_dockerfile_detected to reflect that a bare
Dockerfile below the reporting threshold is not reported, while preserving its
existing setup and assertion.
- Line 17: Update the four git staging subprocess.run calls in
test_language_detector.py and the corresponding staging call in
test_assessors_code_quality.py to pass an explicit check argument, matching the
existing _git_init convention; preserve their current commands and
capture_output behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f8083bd5-512e-49a3-bcaa-1ed9fe8effad

📥 Commits

Reviewing files that changed from the base of the PR and between cf2229e and 1c4f41f.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock, !uv.lock
📒 Files selected for processing (12)
  • .agentready-config.example.yaml
  • docs/attributes.md
  • src/agentready/assessors/__init__.py
  • src/agentready/assessors/code_quality.py
  • src/agentready/assessors/structure.py
  • src/agentready/data/.agentready-config.example.yaml
  • src/agentready/data/default-weights.yaml
  • src/agentready/models/config.py
  • src/agentready/services/language_detector.py
  • tests/unit/test_assessors_code_quality.py
  • tests/unit/test_assessors_structure.py
  • tests/unit/test_language_detector.py

Comment thread tests/unit/test_language_detector.py Outdated
Comment thread tests/unit/test_language_detector.py Outdated
Heavy use of suppression directives (//nolint, # noqa, eslint-disable,
@SuppressWarnings, etc.) degrades lint as a quality signal for AI agents
— lint passes, but not because the code is clean.

New LintSuppressionAssessor (Tier 3, 2% weight) scans source files for
suppression directives across 10 languages, normalized per 1,000 LOC:
- ≤5/1k  → score 100, pass
- 5–15/1k → linear 100→0, fail
- ≥15/1k → score 0, fail

Thresholds and test-file exclusion are configurable via
.agentready-config.yaml::assessor_options.lint_suppression_density.

Closes ambient-code#510
@msu8

msu8 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Hmm, a very suspicious CI fail with 7k lines

@kami619

kami619 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Hmm, a very suspicious CI fail with 7k lines

@msu8 ignore that, it was from a bad merge. I overlooked this CI failure from a different PR., I shall post a fix soon and you can rebase on your end.

@msu8

msu8 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Got it. Anyway, I think I addressed both your review and the coderabbit stuff in the last two commits.

@kami619
kami619 merged commit 56f3c00 into ambient-code:main Jul 28, 2026
5 of 6 checks passed
github-actions Bot pushed a commit that referenced this pull request Jul 28, 2026
# [2.52.0](v2.51.0...v2.52.0) (2026-07-28)

### Features

* **assessors:** add lint suppression density assessor ([#518](#518)) ([56f3c00](56f3c00)), closes [#510](#510)
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.52.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Add lint suppression density check to detect backpressure erosion

2 participants